/*
 * Copyright 2011 Dominik Pretzsch <dominik.pretzsch at gmail.com>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.blacksheep.jmoteextract.mvc.model;

import java.rmi.RemoteException;
import java.util.regex.Pattern;
import de.blacksheep.jmoteextract.archive.ArchiveStorage;
import de.blacksheep.jmoteextract.mvc.view.MainView;
import de.blacksheep.jmoteextract.remote.IRemoteObserver;
import de.blacksheep.jmoteextract.remote.RemoteArchive;
import de.blacksheep.jmoteextract.remote.RemoteServer;
import de.blacksheep.jmoteextract.remote.IRemoteServer;
import de.blacksheep.jmoteextract.remote.RemoteProgressObserver;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbException;
import jcifs.smb.SmbSession;

/**
 * This is the model for the local GUI.
 * 
 * @author Dominik Pretzsch <dominik.pretzsch at gmail.com>
 */
public class MainModel {
    
    /**
     * Prefix used by Windows sytems for network
     * paths. Without escapeing it is "\\".
     */
    //private static final String WIN_NET_SHARE_PREFIX = "\\\\";
    
    /**
     * A helper used to get a list of network drives
     * connected to the local machine.
     */
    //private NetworkDriveList drive;
    
    private static final Logger LOGGER =  Logger.getLogger(MainModel.class.getName());
    
    public MainModel() {
        //this.drive = new NetworkDriveList();
    }
    
    public NtlmPasswordAuthentication obtainAuthorization(String host, String user, String pass) {
        NtlmPasswordAuthentication auth = null;
        try {
            //jcifs.util.LogStream.setLevel(10);            
            
            String smbUrl;
            if (pass == null) {
                smbUrl = String.format("%s", user, pass);
            } else {
                smbUrl = String.format("%s:%s", user, pass);
            }
            
            UniAddress hostAddr = UniAddress.getByName(host);            
            auth = new NtlmPasswordAuthentication(smbUrl);
            
            // ISSUE: Sometimes this does not work.
            //        It is possible to face an error stating
            //        "Unkown user name or bad password", for
            //        no known reason - so far.
            //        Issued when communicating between Win7 -> XP.
            SmbSession.logon(hostAddr, auth);
        } catch (SmbException ex) {
            auth = null;
            Logger.getLogger(MainView.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            Logger.getLogger(MainView.class.getName()).log(Level.SEVERE, null, ex);
        }
        return auth;
    }
    
    /**
     * Returns whether a given drive letter is a
     * network drive.
     * 
     * @param driveLetter   Drive letter (e.g. "C").
     * @return true     is network drive
     * @return false    is not a network drive
     */
    //public boolean isRemoteDrive(String driveLetter) {
    //    return drive.isNetworkDrive(driveLetter);
    //}
    
    /**
     * Returns whether a given path is a network
     * or remote path.
     * 
     * @param path  Absolute file path
     * @return true     is network path
     * @return false    is not a network path
     */
    //public boolean isRemotePath(String path) {
    //    String prefix = path.substring(0, 2);
    //    
    //    if (!prefix.equals(WIN_NET_SHARE_PREFIX))
    //        return isRemoteDrive(prefix.substring(0, 1));
    //    else
    //        return true;
    //}
    
    /**
     * Enqueues an rArchive to the {@see ExtractionQueue}. <br>
     * This method invokes a remote call to a JMoteExtract <br>
     * instance on the network using RMI.
     * 
     * @param pathUNC  Absolute file path
     */
    public void extractOnRemoteHost(String pathUNC) {
        LOGGER.log(Level.FINE, "Getting server name from UNC path");
        String serverName = getServerNameFromUNC(pathUNC);
        
        LOGGER.log(Level.FINE, "Getting RemoteServer stub");
        IRemoteServer server = RemoteServer.getServer(serverName);
        
        if (server == null) {
            // TODO: Give user feedback that no server could be retrieved
            LOGGER.log(Level.SEVERE, "Couldn't get RemoteServer stub");
            return;
        }
        
        RemoteArchive rArchive = new RemoteArchive();
        rArchive.setPathUNC(pathUNC);
        
        try {
            LOGGER.log(Level.FINE, "Sending RemoteArchive to server");
            int hash = server.addArchive(rArchive);
            
            LOGGER.log(Level.FINE, "Creating RemoteObserver object");
            IRemoteObserver remoteObserver = new RemoteProgressObserver(hash);      
            
            LOGGER.log(Level.FINE, "Observing RemoteArchive in local storage");
            ArchiveStorage as = ArchiveStorage.getInstance();
            as.createNonLocalArchive(hash, rArchive);
            
            LOGGER.log(Level.FINE, "Caching RemoteObserver object in local storage");
            as.cacheObserver(hash, remoteObserver);
            
            LOGGER.log(Level.FINE, "Adding RemoteObserver object to remote storage");
            server.assignObserver(hash, remoteObserver);
        } catch (RemoteException ex) {
            // TODO: RemoteException
            LOGGER.log(Level.SEVERE, "RemoteException", ex);
        }
    }
    
    public String getServerNameFromUNC(String unc) {
        String pattern = Pattern.quote(System.getProperty("file.separator"));
        
        // String[] { "", "", server name/IP, [...] }
        String[] split    = unc.split(pattern);
        String serverName = split[2];
        
        return serverName;
    }
    
    public String getServerName(String server) {
        if (isUncServerName(server) || isUncPath(server)) {
            server = getServerNameFromUNC(server);
        }
        return server;
    }
    
    public boolean isUncServerName(String resource) {
        return resource.matches("^[\\\\]{2}[a-zA-Z0-9_\\.]+[\\\\]?");
    }
    
    public boolean isUncPath(String resource) {
        return resource.matches("^[\\\\]{2}[a-zA-Z0-9_\\.]+([\\\\][a-zA-Z0-9_\\.]+)+");
    }
}
